﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using System.IO;

namespace SOMClustering
{
    public partial class FormSOMClustering : Form
    {
        const int patternLength = 3;
        const int mapWidth = 5;
        const int mapHeight = 5;
        private ImageContainer imgContainer;
        private SOM som;
        private Cluster[,] clusters;
        String NameBase = "Кластеризация";
        String AppName = "Кластеризатор изображений";
        String projectName;
        String folderName;
        int dx, dy;

        public FormSOMClustering()
        {
            InitializeComponent();

            Reset();
        }

        private void Reset()
        {
            imgContainer = new ImageContainer(patternLength);
            clusters = new Cluster[mapHeight, mapWidth];
            som = null;
            for (int i = 0; i < mapHeight; i++)
                for (int j = 0; j < mapWidth; j++)
                    clusters[i, j] = new Cluster();
        }

        private void FormSOMClustering_Load(object sender, EventArgs e)
        {
            folderName = Environment.GetFolderPath(
                Environment.SpecialFolder.MyDocuments) + "\\SOM\\";
            int i = 1;
            while (System.IO.Directory.Exists(folderName + NameBase + i.ToString()))
                i++;
            projectName = NameBase + i.ToString();
        }

        private void tsbProcessFolder_Click(object sender, EventArgs e)
        {
            if (fbDialog.ShowDialog() == DialogResult.OK)
            {
                Reset();
                ResetInterface();
                listViewImagePatterns.Visible = false;
                splitContainerMain.Panel2Collapsed = true;
                // Загружаем рисунки в контейнер и параллельно заполняем список миниатюр
                // в отдельном потоке
                ProgressWindow progress = new ProgressWindow();
                OnProgressDelegate onProgress = new OnProgressDelegate(progress.OnProgress);
                onProgress += new OnProgressDelegate(OnFolderProgress);
                imgContainer.FromDirectory(fbDialog.SelectedPath, onProgress);
                progress.ShowDialog();
                progress.Close();
                listViewImagePatterns.Visible = true;
            }
        }

        private void tsbNewProject_Click(object sender, EventArgs e)
        {
            ProjectCreateWindow newWindow = new ProjectCreateWindow();
            newWindow.newProjectAccepted += new OnNewProjectAccepted(OnNewProjectAccepted);
            newWindow.NameBase = NameBase;
            newWindow.ShowDialog();
        }

        private void OnNewProjectAccepted(NewProjectAcceptedEventArgs e)
        {
            projectName = e.ProjectName;
            folderName = e.ProjectFolderName;
            Reset();
            ResetInterface();
        }

        private void ResetInterface()
        {
            listViewImagePatterns.Items.Clear();
            listViewClusterMembers.Items.Clear();
            Text = projectName + " - " + AppName;
            textBoxComment.Text = String.Empty;
        }

        private void OnFolderProgress(int max, int val, String msg, Bitmap bmp)
        {
            
            if (listViewImagePatterns.InvokeRequired)
            {
                listViewImagePatterns.Invoke(new OnProgressDelegate(OnFolderProgress), 
                    new object[4] { max, val, msg, bmp });
            }
            else
            {
                imageListPatterns.Images.Add(msg, bmp);
                listViewImagePatterns.Items.Add(
                    msg, 
                    ImageContainer.FilePathToName(msg), 
                    val - 1);
            }
        }

        private void tsbCluster_Click(object sender, EventArgs e)
        {
            if (imgContainer.Count > 0)
            {
                Boolean recluster = false;
                if (som != null)
                    if (MessageBox.Show(
                        "Изображения уже кластеризованы. Повторить кластеризацию?",
                        "Кластеризация",
                        MessageBoxButtons.OKCancel,
                        MessageBoxIcon.Question) == DialogResult.OK)
                        recluster = true;
                if ((som == null) || recluster)
                {
                    som = new SOM(patternLength, mapWidth, mapHeight, false, null);
                    List<IPattern> patterns = new List<IPattern>();
                    foreach (ImagePattern img in imgContainer.Images)
                    {
                        patterns.Add(img);
                    }

                    ProgressWindow progress = new ProgressWindow();
                    progress.ProcessName = "CLUSTERING";
                    som.Clustering(patterns, new OnClusteringProgress(progress.OnClusteringProgress));
                    splitContainerMain.Panel2Collapsed = false;
                    progress.ShowDialog();
                    DistributeImages();
                }
            }
        }

        private void DistributeImages()
        {
            for (int i = 0; i < mapHeight; i++)
                for (int j = 0; j < mapWidth; j++)
                    clusters[i, j].PatternIndices.Clear();
            foreach (ImagePattern pattern in imgContainer.Images)
            {
                pattern.ClusterCoordinates = som.Coordinates(pattern);
                clusters[pattern.ClusterCoordinates.Y, pattern.ClusterCoordinates.X].PatternIndices.Add(imgContainer.Images.IndexOf(pattern));
            }
        }

        private void tsbSaveProject_Click(object sender, EventArgs e)
        {
            ProjectCreateWindow saveWindow = new ProjectCreateWindow();
            saveWindow.newProjectAccepted += new OnNewProjectAccepted(OnSaveProjectAccepted);
            saveWindow.Text = "Сохранить";
            saveWindow.NameBase = NameBase;
            saveWindow.ShowDialog();
        }

        private void OnSaveProjectAccepted(NewProjectAcceptedEventArgs e)
        {
            folderName = e.ProjectFolderName;
            projectName = e.ProjectName;
            // Весь проект сохраняется

            XmlDocument project = new XmlDocument();

            project.LoadXml("<?xml version=\"1.0\"?>" + "<SOMProject version=\"1.0\" creator=\"SOMClustering\"></SOMProject>");

            String projectPath = folderName + "\\" + projectName;
            if (!Directory.Exists(projectPath))
            {
                Directory.CreateDirectory(projectPath);
            }
            XmlDocument pictures = imgContainer.ToXml();
            pictures.Save(projectPath + "\\images.xml");

            Directory.CreateDirectory(projectPath + "\\thumbnails");
            for (int i = 0; i < imgContainer.Count; i++)
            {
                imgContainer.Images[i].Thumbnail.Save(
                    projectPath + "\\thumbnails\\" +
                    imgContainer.Images[i].ThumbnailName);
            }

            XmlNode node;
            
            node = project.CreateElement("images");
            node.InnerText = "images.xml";
            project.DocumentElement.AppendChild(node);

            node = project.CreateElement("map");

            if (som != null)
            {
                XmlDocument map = som.ToXml();
                map.Save(projectPath + "\\map.xml");
                XmlAttribute attr = project.CreateAttribute("generated");
                attr.InnerText = "1";
                node.Attributes.Append(attr);
                node.InnerText = "map.xml";
            }
            else
            {
                XmlAttribute attr = project.CreateAttribute("generated");
                attr.InnerText = "0";
                node.Attributes.Append(attr);
            }
            project.DocumentElement.AppendChild(node);

            node = project.CreateElement("thumbsFolder");
            node.InnerText = "thumbnails";
            project.DocumentElement.AppendChild(node);

            project.Save(projectPath + "\\" + projectName + ".somprj");
        }

        private void listViewImagePatterns_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (listViewImagePatterns.SelectedIndices.Count > 0)
            {
                Point c = imgContainer.Images[listViewImagePatterns.SelectedIndices[0]].ClusterCoordinates;
                ShowClusterElements(c.Y, c.X);
            }
        }

        private void ShowClusterElements(int y, int x)
        {
            listViewClusterMembers.Items.Clear();
            String s = "";
            foreach (int i in clusters[y, x].PatternIndices)
            {
                listViewClusterMembers.Items.Add(
                    imgContainer.Images[i].ImageName, 
                    imgContainer.Images[i].ThumbnailName, 
                    i);
                s += imgContainer[i].ImageName + "\r\n";
            }
            comboBoxX.SelectedIndex = x;
            comboBoxY.SelectedIndex = y;
            textBoxComment.Text = s;
        }

        private void tsbOpenProject_Click(object sender, EventArgs e)
        {
            ofdOpenProject.InitialDirectory = Environment.GetFolderPath(
                Environment.SpecialFolder.MyDocuments) + "\\SOM\\";
            if (ofdOpenProject.ShowDialog() == DialogResult.OK)
            {
                projectName = ImageContainer.FilePathToName(ofdOpenProject.FileName);
                folderName = ImageContainer.FilePathToDirectory(ofdOpenProject.FileName);
                Reset();
                ResetInterface();
                XmlDocument project = new XmlDocument();
                project.Load(ofdOpenProject.FileName);
                //project.DocumentElement.
                foreach (XmlNode node in project.DocumentElement.ChildNodes)
                {
                    switch (node.Name)
                    {
                        case "images":
                            XmlDocument imgs = new XmlDocument();
                            imgs.Load(folderName + "\\" + node.InnerText);
                            imgContainer = new ImageContainer(patternLength, folderName + "\\thumbnails");
                            imgContainer.FromXml(imgs);
                            imageListPatterns.Images.Clear();
                            for (int i = 0; i < imgContainer.Count; i++)
                            {
                                imageListPatterns.Images.Add(imgContainer[i].Thumbnail);
                                listViewImagePatterns.Items.Add(
                                    imgContainer[i].ImageName, imgContainer[i].ThumbnailName, i);
                            }
                            break;
                        case "map":
                            int generated = int.Parse(node.Attributes.GetNamedItem("generated").InnerText);
                            if (generated == 1)
                            {
                                XmlDocument map = new XmlDocument();
                                map.Load(folderName + "\\" + node.InnerText);
                                som = new SOM();
                                som = som.FromXml(map);
                                DistributeImages();
                            }
                            break;
                    }
                }
            }
        }

        private void comboBoxX_SelectedIndexChanged(object sender, EventArgs e)
        {

        }

        private void buttonShowCluster_Click(object sender, EventArgs e)
        {
            ShowClusterElements(comboBoxY.SelectedIndex, comboBoxX.SelectedIndex);
        }

        private void pictureBoxNav_MouseMove(object sender, MouseEventArgs e)
        {
            int width3 = pictureBoxNav.Width / 3;
            int height3 = pictureBoxNav.Height / 3;
            //int dx = e.X / width3 - 1;
            //int dy = e.Y / height3 - 1;
            dx = e.X / width3;
            dy = e.Y / height3;
            int n = dy * 3 + dx;

            switch (n)
            {
                case 0:
                    pictureBoxNav.Cursor = Cursors.PanNW;
                    break;
                case 1:
                    pictureBoxNav.Cursor = Cursors.PanNorth;
                    break;
                case 2:
                    pictureBoxNav.Cursor = Cursors.PanNE;
                    break;
                case 3:
                    pictureBoxNav.Cursor = Cursors.PanWest;
                    break;
                case 4:
                    pictureBoxNav.Cursor = Cursors.NoMove2D;
                    break;
                case 5:
                    pictureBoxNav.Cursor = Cursors.PanEast;
                    break;
                case 6:
                    pictureBoxNav.Cursor = Cursors.PanSW;
                    break;
                case 7:
                    pictureBoxNav.Cursor = Cursors.PanSouth;
                    break;
                case 8:
                    pictureBoxNav.Cursor = Cursors.PanSE;
                    break;
                default:
                    pictureBoxNav.Cursor = Cursors.Default;
                    break;
            }
        }

        private void pictureBoxNav_Click(object sender, EventArgs e)
        {
            int width3 = pictureBoxNav.Width / 3;
            int height3 = pictureBoxNav.Height / 3;
            int newx = comboBoxX.SelectedIndex + dx - 1;
            int newy = comboBoxY.SelectedIndex + dy - 1;
            if ((newx >= 0) && (newx <= 4) && (newy >= 0) && (newy <= 4) && ((dx - 1 != 0)||(dy - 1 != 0)))
            {
                ShowClusterElements(comboBoxY.SelectedIndex + dy - 1, comboBoxX.SelectedIndex + dx - 1);
            }
        }

        private void tsbAbout_Click(object sender, EventArgs e)
        {
            AboutBox1 aboutBox = new AboutBox1();
            aboutBox.ShowDialog();
        }
    }
}